﻿/*
* 一些通用公共方法集合
*/
$.com = new function () {
    'use strict';

    //instead of this
    var that = this;

    /*
    * 用于判断值类型对象是否有值
    * @param { value } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.hasValue = function (value) {
        if (value !== null && value !== '' && value !== undefined && typeof value !== 'undefined') {
            return true;
        }
        return false;
    };

    /*
    * 用于判断值一个类型对象是否为空, 可用于判断值类型, 对象, Json对象, 数组和jQuery对象
    * @param { value } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.isNullOrEmpty = function (value) {
        var flag = false;
        if (!that.hasValue(value)) {
            flag = true;
        } else if (typeof value === 'object') {
            try {
                if (value instanceof Array && value.length === 0) {
                    //array
                    flag = true;
                } else if (value instanceof jQuery) {
                    //jQuery
                    flag = value.hasOwnProperty('length') && value.length === 0 || !value.hasOwnProperty('length');
                } else {
                    // if value is json data
                    value = JSON.stringify(value);
                    if (value === '' || value === '{}') {//为空
                        flag = true;
                    }
                }
            } catch (e) {
                flag = false;
            }
        }
        return flag;
    };

    /**
     * 判断类型是否 js 的简单类型
     * @param {any} value
     */
    this.isSimpleType = function (value) {
        var type = typeof value;
        return type === 'number' || type === 'string' || type === 'boolean' || (type === 'object' && value instanceof Date);
    };

    /*
    * 用于判断一个值是否 array 并且有值
    * @param { value } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.isArray = function (value) {
        return value instanceof Array && value.hasOwnProperty('length') && value.length > 0;
    };

    /*
    * 用于判断一个值是否 function
    * @param { value } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.isFunction = function (value) {
        return value && typeof value === 'function';
    };

    /*
    * 用于判断一个值不是 array 但类似 array 并且有值
    * @param { obj } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.isArrayLike = function (obj) {
        var length = !!obj && "length" in obj && obj.length,
            type = jQuery.type(obj);

        if (type === "function" || jQuery.isWindow(obj)) {
            return false;
        }

        return type === "array" || length === 0 ||
            typeof length === "number" && length > 0 && (length - 1) in obj;
    }

    /*
    * 用于判断一个值是否jquery对象
    * @param { value } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.isJqueryObject = function (value) {
        return value instanceof jQuery && value.hasOwnProperty('length') && value.length > 0;
    };

    /*
    * 用于判断一个值是否 guid 数据类型
    * @param { value } 需要判断的值
    * @returns { bool } 返回判断结果
    */
    this.isGuid = function (value) {
        if (typeof value === 'string') {
            var pattern = /^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$/;
            return pattern.test(value.toUpperCase());
        }

        return false;
    };

    /*
    * 对于jquery的类型判断添加 guid 的支持
    * @param { value } 需要判断的值
    * @param { type } 需要判断的值的原始类型
    * @returns { string } 返回判断结果
    */
    this.type = function (value, type) {
        if ($.com.isGuid(value)) {
            return 'guid';
        }

        var t = $.type(value);

        if ($.com.hasValue(type)) {
            return t === type ? t : type;
        }

        return t;
    };

    /*
    * 使用当前时间获取一个随机数, 使用系统时间分钟秒毫秒部分加随机数
    * @param { length } 随机数长度
    * @returns { String } 返回产生的字符串随机数
    */
    this.getSerial = function (length) {
        var serial = new Date().format('mmssSSS');
        var random = Math.random().toString();
        length = random.length >= length ? length : random.length;
        if (random.indexOf('.') > 0) {
            random = random.substr(3, length);
            serial += random;
        }

        return serial;
    };

    /*
    * 获取一个对象属性的数量/数组的长度
    * @param { data } 对象或数组
    * @returns { Number } 返回对象的属性数量, 或数组长度
    */
    this.getPropertyCount = function (data) {
        var count = 0;

        if ($.com.hasValue(data)) {
            if ($.com.isArray(data)) {
                count = data.length;
            } else {
                $.each(data, function () {
                    count++;
                });
            }
        }

        return count;
    };

    /*
    * 对一个对象/数组进行处理, 移除其中无效数据(无效数据指: serial 属性, 空属性, 等)
    * @param { data } 对象或数组
    * @param { keepReference } 是否保留引用
    * @returns { data } 返回处理后的数据
    */
    this.cleanData = function (data, ignoreFields) {
        var d;
        var v;
        ignoreFields = ignoreFields || _config_.default.ignoreFields;
        if (!that.isSimpleType(data)) {
            if (that.isArrayLike(data)) {
                d = [];
                $.each(data, function (key, value) {
                    v = that.cleanData(value, ignoreFields);
                    if (v !== undefined) {
                        d.push(v);
                    }
                });
            } else {
                d = {};
                var value, count = 0;
                for (var key in data) {
                    value = data[key];
                    // key 过滤条件: 1. 属于 data 自有属性, 2. 类型为字符串, 3. 不能以 _ 开头, 4. 不能包含 ., 5. 不能包含在预定义的忽略字段列表中
                    if (data.hasOwnProperty(key) && typeof (key) === 'string' && !key.startsWith('_') && !key.includes('.') && !ignoreFields.split(',').includes(key) && !$.com.isNullOrEmpty(value)) {
                        if (that.isSimpleType(value)) {
                            d[key] = value;
                        } else if (typeof value === 'object') {
                            v = that.cleanData(value, ignoreFields);
                            if (v !== undefined) {
                                d[key] = v;
                            }
                        }

                        count++;
                    }
                }

                //如果对象仅剩一个字段, 且这仅有的一个字段是状态标识字段, 则删除该对象
                if (count === 1 && $.com.hasValue(d[_config_.form.defaults.dataStateField])) {
                    d = undefined;
                }
            }
        }

        if ($.com.isNullOrEmpty(d)) {
            d = undefined;
        }

        return d;
    }

    /*
    * 判断一个对象是否包含 options.data 属性
    * @param { el } 待判断对象
    * @returns { bool } 返回判断结果
    */
    this.hasOptionsData = function (el) {
        return $.com.hasValue(el) && $.com.hasValue(el.options) && $.com.hasValue(el.options.data);
    };

    /*
    * 返回一个form的数据
    * @param { el } form元素
    * @returns { bool } 返回form的options.data
    */
    this.getFormData = function (el) {
        var $el = $(el);
        if ($.com.isJqueryObject($el) && $el.isForm()) {
            var formData = $el.data(_config_.form.namespace);
            if ($.com.hasOptionsData(formData)) {
                return formData.options.data;
            }
        }
    };

    /*
    * 产生 m 到 n 之间的随机整数
    * @param { m } 随机整数起始值
    * @param { n } 随机整数结束值
    * @returns { Number } 返回 m 到 n 之间的随机整数
    */
    this.getRandomInt = function (m, n) {
        m = Number(m || 0);
        n = Number(n || 0);
        var w = n - m;
        return Math.round(Math.random() * w + m);
    };

    /*
    * 将输入的字符串反射成对象, 数组或者方法等
    * @param { value } 任意输入值, 非字符串将原样返回
    * @returns { } 返回反射结果
    */
    this.eval = function (value, prefix) {
        if (that.hasValue(value) && typeof value === 'string') {
            try {
                //if string content is function, array
                if (that.hasValue(prefix) && value.startsWith(prefix)) {
                    value = value.substring(prefix.length);
                }

                if ($.com.isGuid(value)) {
                    return value;
                }

                return eval(value);
            } catch (errer) {
                try {
                    //if string content is json object
                    return eval('(' + value + ')');
                } catch (errer) {
                    return undefined;
                }
            }
        }

        return value;
    };

    /*
    * TBC
    * 将输入的字符串反射成对象, 数组或者方法等并执行返回, 若为方法, 方法参数将接受到第二个开始的参数
    * @param { value } 任意输入值, 非字符串将原样返回
    * @returns { } 返回反射并执行的结果
    */
    this.evalResult = function (value) {
        value = $.com.eval(value);
        if ($.com.hasValue(value)) {
            if (typeof value === 'function') {
                var target = arguments[1];
                var args = Array.prototype.slice.call(arguments, 1);
                return value.apply(target, args);
            }

            return value;
        }
    };

    /*
    * 将输入的字符串反射成方法
    * @param { value } 任意字符串, 非字符串将原样返回
    * @returns { } 返回反射结果, 如方法不存在或者反射失败, 则返回 undefined
    */
    this.getFunction = function (value) {
        value = that.eval(value);
        if ($.isFunction(value)) {
            return value;
        }
        return undefined;
    };

    /*
    * 一组预定义的带参数正则表达式
    */
    this.regulars = {
        email: /[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?/,
        url: /^(https?:\/\/([\w-]+\.)+[\w-]+(\/[\w-\.\/?%&=]*)?)$/,
        telephone: /(((\d{2}-)?(0\d{2,3}-)?\d{7,8}(-\d{4})?)|([4|8]00-\d{3}-\d{4}))/,
        mobilephone: /^(1[3|5|6|7|8]\d{9})$/,
        float: '^(0|({sy}0\\.(\\d{{nl},{xl}}))|({sy}[1-9]\\d*\\.?\\d{{nl},{xl}}))$',
        number: '^(0|{sy}[1-9][0-9]{{nl},{xl}})$',
        length: '^\\w{{nl},{xl}}$'
    };
    /*
    * 将预定义正则表达式的参数转化后的正则表达式
    */
    this.predefinedRegular = function (pattern) {
        var xl = 1;         //maxLength
        var nl = 1;         //minLength
        var sy = '';        //symbol, +, - and other

        var rule = pattern;
        if (typeof rule === 'string' && rule.indexOf(',') > 0) {
            var params = rule.split(',');
            if (params.length >= 1) {
                rule = params[0];
            }
            if (params.length >= 2) {
                xl = params[1];
            }
            if (params.length >= 3) {
                nl = params[2];
            } else {
                if (rule === 'length') {
                    nl = 0;
                }
            }
            if (params.length >= 4) {
                sy = params[3];
            }
        }

        if (rule === 'number') {
            xl--;
            nl--;

            if (sy) {
                sy = (sy === '-' || sy === '+') ? sy : '-?';
                sy = (sy === '+') ? '' : sy;
            } else {
                sy = '-?';
            }
        }

        var rule = $.com.regulars[rule];
        if ($.com.hasValue(rule) && typeof rule === 'string') {
            rule = rule.replace(/{sy}/g, sy);
            rule = rule.replace(/{xl}/g, xl);
            rule = rule.replace(/{nl}/g, nl);
        }

        return rule || pattern;
    };
    /*
    * 使用预定义的带参数正则表达式验证输入值
    * @param { rule } 预定义的正则表达式名称, 参数表达式形式为 (float, -, 4, 2) 意为: 输入值按照负小数验证, 小数位最长4位, 最短2位
    * @param { data } 待验证的值
    * @returns { bool } 返回验证结果
    */
    this.validValue = function (rule, data) {
        if (that.hasValue(rule) && that.hasValue(data)) {
            if (typeof rule === 'string') {
                rule = $.com.predefinedRegular(rule);
                rule = new RegExp(rule);
            }

            return rule instanceof RegExp && rule.test(data);
        }

        return false;
    };

    /*
    * 从 object data 上获取绑定的数据
    * @param { data } 包含数据的 object 对象
    * @param { arg } 标志开关,接受参数顺序: 传入参数>>data-parameter-arg属性值>>默认值  默认取 node 属性
    * @param { prefix } 绑定标志开关, 接受参数顺序: 传入参数>>data-parameter-prefix属性值>>默认值. 默认取 #: 前缀
    * @returns { } 返回获取结果
    */
    this.getDataBind = function (data, arg, prefix) {
        if (that.hasValue(data)) {
            prefix = prefix || data[_config_.default.parameterPrefix] || _config_.default.parameterPrefixValue;
            $.each(data, function (k, v) {
                if (typeof v === 'string' && v.startsWith(prefix)) {
                    try {
                        var value = v.substring(prefix.length);
                        data[k] = that.eval(value);
                        if (!that.hasValue(data[k]) && that.isJqueryObject($(value))) {
                            data[k] = that.getDataFromElement(value, arg, prefix);
                        }
                    } catch (e) {
                        delete data[k];
                    }
                }
            });
        }

        return data;
    };

    /*
    * 从当前 html 元素节点上获取绑定的数据
    * @param { $el } 包含数据的 dom 元素
    * @param { arg } 标志开关,接受参数顺序: 传入参数>>data-parameter-arg属性值>>默认值  默认取 node 属性
    * @param { prefix } 绑定标志开关, 默认取 : 前缀
    * @returns { } 返回获取结果
    */
    this.getDataBindFromElement = function ($el, arg, prefix) {
        $el = $($el);
        if (that.isJqueryObject($el)) {
            prefix = prefix || $el.attr(_config_.attribute.parameterPrefix) || _config_.default.parameterPrefixValue;
            return that.getDataBind($el.data(), arg, prefix);
        }
    };

    /*
    * 从当前 html 元素节点上获取整个节点树绑定的数据
    * @param { $el } 包含数据的 dom 元素
    * @param { arg } 标志开关,接受参数顺序: 传入参数>>data-parameter-arg属性值>>默认值  默认取 node 属性
    * @param { prefix } 绑定标志开关, 接受参数顺序: 传入参数>>data-parameter-prefix属性值>>默认值. 默认取 #: 前缀
    * @returns { } 返回获取结果
    */
    this.getDataFromElement = function ($el, arg, prefix) {
        $el = $($el);

        if (that.isJqueryObject($el)) {
            arg = arg || $el.attr(_config_.attribute.parameterArg) || _config_.attribute.parameterArgNode;

            //var data = $el.data();
            var data;
            var isArray = $el.attr(_config_.attribute.parameterArgNodeType) === _config_.default.arrayTypeName;
            if (isArray) {
                data = [];
            } else {
                data = that.getDataBindFromElement($el, arg, prefix);
            }

            var children = $el.children();
            if (that.hasValue(data) && that.isJqueryObject(children)) {
                $.each(children, function () {
                    var value = that.getDataFromElement(this);
                    if (that.hasValue(value)) {
                        if (isArray) {
                            data.push(value);
                        } else {
                            var child = $(this).attr(arg);
                            if (that.hasValue(child)) {
                                data[child] = value;
                            }
                        }
                    }
                });
            }

            return data;
        }
    };

    /*
    * 获取 data 对象的 key 属性值, 支持 a.b.c 形式
    * 现在可以支持从数组中直接取出值, 形式为 a.b[1].c
    * @param { data } 需要获取值的原始 data 对象
    * @param { key } 要获取的属性名称
    * @returns { } 返回获取结果, 无结果或属性错误则返回 undefined
    */
    this.getValue = function (data, key) {
        if (!that.isNullOrEmpty(data) && that.hasValue(key) && typeof data === 'object' && typeof key === 'string') {
            var index = key.indexOf('.');
            if (index > 0) {
                var cur = key.substr(0, index);
                key = key.replace(cur + '.', '');
                var value = data[cur];
                if (cur.includes('[') && cur.includes(']')) {
                    value = cur.split(/[\[|\]]/);
                    index = value[1];
                    cur = value[0];
                    value = data[cur];
                    if ($.com.hasValue(value) && $.com.hasValue(value.length) && $.com.hasValue(index) && Number(index) >= 0) {
                        value = value[Number(index)];
                    }
                }

                data = that.getValue(value, key);
            } else {
                data = data[key];
            }

            return data;
        }
    };

    /*
    * 给 data 对象的 key 属性赋值 value, 支持 a.b.c 形式, 支持覆盖和合并模式
    * @param { data } 需要设置值的原始 data 对象
    * @param { key } 要设置的属性名称
    * @param { value } 要设置的属性值
    * @param { mode } 设置模式, 支持覆盖和合并模式, 默认覆盖
    * @returns { } 返回设置后的 data 对象
    */
    this.setValue = function (data, key, value, mode) {
        if (that.hasValue(data) && that.hasValue(key) && typeof data === 'object' && typeof key === 'string') {
            var index = key.indexOf('.');
            if (index > 0) {
                var cur = key.substr(0, index);
                key = key.substring(index + 1, key.length);
                var curData = data[cur];
                if (cur.includes('[') && cur.includes(']')) {
                    var curValue = cur.split(/[\[|\]]/);
                    index = curValue[1];
                    cur = curValue[0];
                    curData = data[cur]
                    if (!$.com.hasValue(curData) || !$.com.hasValue(curData.length) || curData.length <= 0) {
                        data[cur] = [{}];
                    }
                    curData = data[cur];
                    if ($.com.hasValue(curData) && $.com.hasValue(curData.length) && $.com.hasValue(index) && Number(index) >= 0) {
                        curData = curData[Number(index)];
                        curData = that.setValue(curData, key, value, mode);
                    }
                } else {
                    if (!$.com.hasValue(curData)) {
                        data[cur] = {};
                        curData = data[cur];
                    }

                    that.setValue(curData, key, value, mode);
                }
            } else {
                if (that.hasValue(mode) && !mode && typeof value === 'object') {
                    data[key] = $.extend(data[key], value);                         // merge
                } else {
                    data[key] = value;                                              // cover
                }
            }
        }
    };

    /*
    * 通过总数目和页大小, 计算总页数
    * @param { total } 总数目
    * @param { size } 页大小
    * @returns { Number } 返回总页数
    */
    this.getPageCount = function (total, size) {
        if ($.isNumeric(total) && $.isNumeric(size)) {
            return Math.floor((total - 1) / size) + 1;
        }
        return 0;
    };

    /*
    * 对输入数据 value 进行小数位 length 长度四舍五入, 如果 value 的小数位数超过 length, 则会四舍五入, 不超过, 则不会补零
    * @param { value } 原始数字
    * @param { length } 小数位最高长度
    * @returns { Number } 四舍五入后的结果
    */
    this.toFixed = function (value, length) {
        if (that.hasValue(value)) {
            value = value.toString();
            length = length || 0;
            var index = value.indexOf('.');
            var posion = value.length - index - 1;
            if (index > 0) {
                length = length > posion ? posion : length;
            } else {
                length = 0;
            }
            return Number(value).toFixed(length);
        }
    };

    /*
    * 对输入数据 value 进行小数位 length 长度四舍五入, 如果 value 的小数位数超过 length, 则会四舍五入, 不超过, 则会补零
    * @param { value } 原始数字
    * @param { length } 小数位最高长度
    * @returns { Number } 四舍五入后的结果
    */
    this.round = function (value, length) {
        value = (value || 0).toString();
        length = length || 0;
        if ($.isNumeric(value) && $.isNumeric(length)) {
            try {
                length = Number(length);
                if (length >= 0) {
                    value = that.toFixed(value, length).toString();
                    var index = value.lastIndexOf('.');
                    if (length > 0) {
                        if (index < 0) {
                            value = value + '.';
                        }
                        index = value.length - value.lastIndexOf('.') - 1;
                        return value.padEnd(value.length + length - index, '0');
                    }
                }
                return value.toString();
            } catch (e) {
                return value.toString();
            }
        }
    };

    /*
    * 人民币转大写
    * @param { money } 原始人民币数字
    * @returns { String } 字符串格式大写人民币金额
    */
    this.moneyToUpper = function (money) {
        money = (money || 0).toString();
        if ($.isNumeric(money)) {
            var cnNumber = "零壹贰叁肆伍陆柒捌玖";
            var result = '';
            try {
                for (var i = 0; i < money.length; i++) {
                    if (money[i] === '.') {
                        result += '点';
                    } else {
                        result += cnNumber[Number(money[i])];
                    }
                }
            } catch (e) {
                result = undefined;
            }
            return result;
        }
    };

    /*
    * 移除元素的 class 名, 查找包含 className 的批量移除
    * @param { element } 要处理的元素
    * @param { className } 类名
    * @returns 无返回
    */
    this.removeClass = function (element, className) {
        if (!that.isNullOrEmpty(element) && that.hasValue(className)) {
            element.removeClass(function (i, x) {
                if (that.hasValue(x)) {
                    return x.split(' ').first(function (f) {
                        return f.includes(className);
                    });
                }
            });
        }
    };
    /**
     * 根据当前 href 或者参数值转换对象和 queryString 字符串
     * @param {any} value
     */
    this.getQueryString = function (value) {
        if (typeof value === 'string') {
            //字符串类型则为从字符串转换为 queryString 对象
            var result = {};
            value = value || location.href;
            var index = value.indexOf('?');
            if (index > 0) {
                result.url = value.substring(0, index);
            } else {
                result.url = value;
            }
            var queryString = value.match(new RegExp("[\?\&][^\?\&]+=[^\?\&]+", "g"));
            if ($.com.isArray(queryString)) {
                for (var i = 0; i < queryString.length; i++) {
                    var cur = queryString[i];
                    cur = cur.substring(1);
                    if ($.com.hasValue(cur)) {
                        index = cur.indexOf('=');
                        if (index > 0) {
                            result[cur.substring(0, index)] = cur.substring(index + 1, cur.length);
                        }
                    }
                }
            }

            return result;
        } else if (typeof value === 'object') {
            //对象串类型则从 queryString 对象转换为字符串
            let result = [];
            for (var key in value) {
                const cur = value[key];
                if ($.com.hasValue(cur)) {
                    result.push(`${key}=${cur}`);
                }
            }

            if (that.isArray(result)) {
                return '?' + result.join('&');
            }
        }
    };

    //根据QueryString参数名称获取值
    this.getQueryStringByName = function (name, url) {
        var result = $.com.getQueryString(url);
        if ($.com.hasValue(result)) {
            return result[name];
        }
    };

    this.formatMonery = function (value, format) {
        if (that.hasValue(value)) {
            try {
                if (typeof value === 'object' && $.com.hasValue(value.options)) {
                    format = format || value.options.format;
                    value = value.options.value;
                }
                if ($.com.isNullOrEmpty(value)) {
                    return '';
                }
                if ((!$.com.hasValue(format) || typeof format !== 'string')) {
                    return '';
                } else {
                    return numeral(value).format(format);
                }
            } catch (errer) {
                return undefined;
            }
        }

        return undefined;
    };

    //DateTime format to string
    //odata datetime format is 'yyyy-MM-ddTHH:mm:ssZ'
    this.dateTimeFormatter = function (value, format) {
        if (that.hasValue(value)) {
            try {
                if (typeof value === 'object' && $.com.hasValue(value.options)) {
                    format = format || value.options.format;
                    value = value.options.value;
                }

                if (typeof value === 'string') {
                    // value = value.replace(/T|t|Z|z/g, ' ').replace(/\-/g, '\/');
                    //if (value.includes('.')) {
                    //    value = value.substring(0, value.indexOf('.'));
                    //}

                    value = new Date(value);
                }

                if (!$.com.hasValue(format) || typeof format !== 'string') {
                    format = _config_.format.monemt.datetime;
                }

                if (typeof value === 'object' && value instanceof Date) {
                    return value.format(format);
                }
            } catch (errer) {
                return undefined;
            }
        }

        return undefined;
    };

    //date format to string
    this.dateFormatter = function (value) {
        return that.dateTimeFormatter(value, _config_.format.monemt.date);
    };

    //time format to string
    this.timeFormatter = function (value) {
        return that.dateTimeFormatter(value, _config_.format.monemt.time);
    };

    //field element formatter, format data from options.dataSource.data
    this.fieldFormatter = function (field) {
        if (typeof field === 'object' && $.com.hasValue(field.options)) {
            var data = $.com.getValue(field, 'options.dataSource.data');
            if ($.com.isArray(data)) {
                return data.firstValue(function (x) { return x[field.options.dataValueField] === field.options.value; }, field.options.dataTextField);
            }
        }

        return value;
    };

    //将数据中对象的深层次键和值转换为字符串形式的键, 如 {A:{B:'x'}} 转为 {A:{B:'x'}, A.B:'x'} 形式
    this.convertToString = function (obj, top, key) {
        if ($.com.hasValue(obj)) {
            if ($.com.isArray(obj)) {
                for (var i = 0; i < obj.length; i++) {
                    if (typeof obj[i] === 'object') {
                        $.com.convertToString(obj[i], top, key);
                    }
                }
            } else if (typeof obj === 'object') {
                top = top || obj;
                for (var k in obj) {
                    var cur = key;
                    cur = cur ? (cur + '.' + k) : k;
                    if (typeof obj[k] === 'object') {
                        $.com.convertToString(obj[k], top, cur);
                    } else {
                        top[cur] = obj[k];
                    }
                }
            }
        }
    };

    //TBC 与上面的方法正好相反, 将对象中字符串结构的键转换为对象形式的键
    this.convertToObject = function (obj) {
        var key, value;
        for (x in obj) {
            key = x;
            value = obj[x];
            if (typeof value === 'object') {
                value = $.com.convertToObject(value);
            }

            obj[key] = value;
        }

        return obj;
    };

    //转换来自服务器的数据, key 和 value 字段用于定义 object 类型转 array 时取 key 和 value 值的字段名
    this.dataAdapter = function (data, key, value) {
        var result = data;
        if (that.hasValue(data) && (that.hasValue(data.value) || that.hasValue(data.items))) {
            result = data.rows || data.value || data.items;
        }

        if (!that.isSimpleType(result) && !that.isArray(result)) {
            result = that.objectToArray(result, key, value)
        }

        return result;
    };

    //转换来自服务器的数据为 odata-v4 格式
    this.odataAdapter = function (data) {
        if (that.hasValue(data) && !that.hasValue(data.rows)) {
            if ($.com.hasValue(data.items)) {
                data.rows = data.items;
                delete data.items;
            }

            if ($.com.hasValue(data.content)) {
                data.rows = data.content;
                delete data.content;
            }

            if ($.com.hasValue(data.value)) {
                data.rows = data.value;
            }

            if ($.com.hasValue(data['@odata.count'])) {
                data.total = data['@odata.count'];
            }
        }

        if ($.com.hasValue(data.rows)) {
            data.value = data.rows;
        }

        if ($.com.hasValue(data.total)) {
            data['@odata.count'] = data.total;
        }

        return data;
    };

    /*
    * 获取一个对象绑定的 kendo 数据源, 此数据源为原始 filter 条件和终态其他条件的集合
    * 此功能主要用于获取 grid 和 field 上绑定的原始查询条件和当前其他查询选项
    * @returns {} 处理后的数据源
    */
    this.getDataSource = function (el) {
        if ($.com.hasValue(el)) {
            if ($.com.hasValue(el.role)) {
                el = el.role;
            }

            if ($.com.hasValue(el.dataSource) && $.com.hasValue(el.initials) && $.com.hasValue(el.initials.dataSource)) {
                return {
                    page: el.dataSource.page() || 1,
                    pageSize: el.dataSource.pageSize(),
                    sort: el.dataSource.sort(),
                    group: el.dataSource.group(),
                    aggregate: el.dataSource.aggregate(),
                    filter: $.extend(true, {}, el.initials.dataSource.filter),
                    //transport: $.extend(true, {}, el.initials.dataSource.transport)
                };
            }
        }
    };

    //获取或合并两个数据源对象, 合并到第一个数据源对象中
    this.getQueryOptions = function (src, dest) {
        if ($.com.hasValue(src) && $.com.hasValue(dest)) {
            var dataSrc = $.extend(true, {}, $.com.getValue(src, 'transport.read.data'));
            var dataDest = $.extend(true, {}, $.com.getValue(dest, 'transport.read.data'));
            src = $.extend(true, src, dataSrc, dataDest);
            $.com.setValue(src, 'transport.read.data', $.extend(true, {}, dataSrc, dataDest));
            src.filter = $.com.mergeFilter(src.filter, dest.filter);
            delete src.transport;
            return src;
        }
    };

    this.mergeFilter = function (src, dest) {
        var filter = $.extend(true, {}, src, dest);
        if (!$.com.isNullOrEmpty(src) && !$.com.isNullOrEmpty(dest)) {
            if ($.com.isArray(src.filters) || $.com.isArray(dest.filters)) {
                //当前只合并filter条件的第一级子条件
                if (!$.com.isArray(src.filters)) {
                    src.filters = [$.extend({}, src)];
                }
                if (!$.com.isArray(dest.filters)) {
                    dest.filters = [$.extend({}, dest)];
                }
                src.filters = src.filters || [];
                dest.filters = dest.filters || [];
                filter = { filters: $.extend(true, [], src.filters.concat(dest.filters)) };
            } else {
                if (src.field === dest.field) {
                    filter = dest;
                } else {
                    filter = { filters: [src, dest] };
                }
            }
        }

        return filter;
    };

    this.getFilters = function (src, dest) {
        if (!$.com.isNullOrEmpty(src) && !$.com.isNullOrEmpty(dest)) {
            if ($.com.isArray(src.filters)) {
                for (var i = 0; i < src.filters.length; i++) {
                    $.com.getFilters(src.filters[i], dest);
                }
            } else {
                $.com.mergeFilters(src, dest);
            }
        } else {
            if ($.com.isNullOrEmpty(src)) {
                src = dest;
            }

            if ($.com.isNullOrEmpty(src)) {
                src = undefined;
            }
        }
    };

    this.mergeFilters = function (src, dest) {
        if (!$.com.isNullOrEmpty(dest)) {
            if ($.com.isArray(dest.filters)) {
                for (var i = 0; i < dest.filters.length; i++) {
                    $.com.mergeFilters(src, dest.filters[i]);
                }
            } else {
                if (src.field === dest.field) {
                    //field 相同, 使用 dest 的条件
                    src = dest;
                } else {
                    //field 不同
                    src = { filters: [src, dest] };
                }
            }
        }
    };

    this.beforeSend = function (options, xhr) {
        that.ajaxBeforeSend(options, xhr);
        if (that.hasValue(options) && typeof options.beforeSend === 'function') {
            if ($.com.hasValue(options.el) && typeof options.el.execute === 'function') {
                return options.el.execute(options.beforeSend, xhr);
            } else {
                return options.beforeSend(xhr);
            }
        }
    };

    this.onSuccess = function (options, data, xhr) {
        if (that.hasValue(options) && typeof options.success === 'function') {
            if ($.com.hasValue(options.el) && typeof options.el.execute === 'function') {
                options.el.execute(options.success, data, xhr);
            } else {
                options.success(data, xhr);
            }
        }
    };

    this.onError = function (options, xhr) {
        if (that.hasValue(options) && typeof options.error === 'function') {
            if ($.com.hasValue(options.el) && typeof options.el.execute === 'function') {
                options.el.execute(options.error, xhr);
            } else {
                options.error(xhr);
            }
        }
    };

    this.onComplete = function (options, xhr) {
        //that.ajaxComplete(options, xhr);
        if (that.hasValue(options) && typeof options.complete === 'function') {
            if ($.com.hasValue(options.el) && typeof options.el.execute === 'function') {
                options.el.execute(options.complete, xhr);
            } else {
                options.complete(xhr);
            }
        }
    };

    this.ajaxBeforeSend = function (context, xhr) {
        //xhr._startTime = new Date();
        //context.url = that.encodeURI(context.url);
    };

    this.ajaxComplete = function (context, xhr) {
        xhr._endTime = new Date();
        var queryableCache = { Value: context.url, Cost: xhr._endTime - xhr._startTime };
        var name = queryableCache.Value;
        var index = name.indexOf('?');
        if (index > 0) {
            name = name.substr(0, index);
        }

        index = name.lastIndexOf('/');
        if (that.isGuid(name.substr(index + 1))) {
            name = name.substr(0, index);
        }

        queryableCache.Name = name;
        console.log(queryableCache.Name + ' 耗时 ' + queryableCache.Cost + ' 毫秒!');
    };

    //构造hash值,两个相同字符串构造出的hash值是相同的
    this.hashCode = function (value) {
        var hash = 0, i, chr;
        if (value.length === 0) return hash;
        for (i = 0; i < value.length; i++) {
            chr = value.charCodeAt(i);
            hash = (hash << 5) - hash + chr;
            hash |= 0; // Convert to 32bit integer
        }
        return hash;
    };

    /*
    * 切换到当前元素所在的选项卡
    * @param { el } 当前元素
    * @returns 无返回
    */
    this.switchTabs = function (el) {
        var $el = $(el);
        if ($.com.isJqueryObject($el) && $el.closest('.tab-pane').hasValue() && $el.closest('.tab-content').hasValue() && $el.closest('.tab-content').prev().is('.nav.nav-tabs')) {
            var $tabPanel = $el.closest('.tab-pane');
            var $tabNavs = $el.closest('.tab-content').prev();
            var $tabNav = $tabNavs.children().filter(function () {
                return $tabPanel.equal($($(this).children().attr('href')));
            });
            if ($tabNav.hasValue()) {
                $tabNav.children().click();
            }
        }
    };

    /*
    * 判断当前元素值是否 bool 值的 true
    * @param { el } 当前元素
    * @param { attr } 要判断的属性
    * @returns 返回当前元素值是否 bool 值的 true
    */
    this.isTrue = function (el, attr) {
        var $el = $(el);
        var result = false;
        var value = $.com.hasValue(attr) ? $el.attr(attr) : ($el.val() || $el.attr("data-value"));

        if ($.com.hasValue(value)) {
            value = value.toUpperCase();
            result = value === "TRUE";
        }

        return result;
    };

    //重新计算高级查询区的尺寸
    this.resizeAdvancedSearch = function ($el) {
        if ($.com.isJqueryObject($el)) {
            var el = $el.data(_config_.button.namespace);
            if (that.hasValue(el)) {
                var $target = $($el.attr('data-target'));
                var width = document.documentElement.clientWidth;
                $target.css('width', width - 60);
                var style = el.options.style;

                if (style === 'dropdown') {
                    $target.css('position', 'inherit');
                }
            }
        }
    };

    //高级查询按钮变色
    this.advancedSearchColorful = function ($el) {
        if ($.com.isJqueryObject($el)) {
            $el.removeClass('has-value');
            var $target = $($el.attr('data-target'));
            var $inputs = $target.find(_config_.selector.field).not('[type="hidden"]');
            if ($inputs.hasValue()) {
                $inputs.each(function () {
                    if ($.com.hasValue($(this).val())) {
                        $el.addClass('has-value');
                        return false;
                    }
                });
            }
        }
    };

    /*
    * 将一个json对象的键值对转换成 Map 的形式
    * @returns 返回转换后的结果
    */
    this.jsonToMap = function (json) {
        var map = new Map();
        //$.each(json, function (key, value) {
        //    map.set(key, value);
        //});
        for (var k in json) {
            map.set(k, json[k]);
        }

        return map;
    };

    this.arrayToMap = function (array) {
        if ($.com.isArray(array)) {
            var arr = new Array();
            for (var i = 0; i < array.length; i++) {
                arr.push($.com.jsonToMap(array[i]));
            }
        }
    };

    /*
     * 将类似于 Array 的对象转换为真正的 Array
     * * 讨厌的 kendoui dataSource *
     */
    this.toArray = function (data) {
        let arr = new Array();
        if (!$.com.isNullOrEmpty(data) && data.length > 0) {
            for (var index = 0; index < data.length; index++) {
                arr.push(data[index]);
            }
        }

        return arr;
    };

    /**
     * 将 json 对象的 key value 转换为 由 key 和 value 指定的字段名的对象数组
     * @param {any} data key 原始对象
     * @param {any} key key 字段名
     * @param {any} value value 字段名
     */
    this.objectToArray = function (data, key, value) {
        if (that.isNullOrEmpty(data)) {
            return [];
        }

        key = key || "key";
        value = value || "value";
        var arr = [];
        for (var k in data) {
            var obj = {};
            obj[key] = k;
            obj[value] = data[k];
            arr.push(obj);
        }

        return arr;
    };

    /*
    * 使用循环以消耗CPU性能的方法延迟当前线程一定的时间
    * @param { n } 延迟时间, 单位毫秒
    * @returns 无返回
    */
    this.sleep = function (n) {
        var start = new Date().getTime();
        while (true) {
            if (new Date().getTime() - start > n) {
                break;
            }
        }
    }

    /*
    * 获取当前 ie 浏览器版本号
    */
    this.ieVersion = function () {
        var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串  
        var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器
        var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器  
        var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
        if (isIE) {
            var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
            reIE.test(userAgent);
            var fIEVersion = parseFloat(RegExp["$1"]);
            if (fIEVersion == 7) {
                return 7;
            } else if (fIEVersion == 8) {
                return 8;
            } else if (fIEVersion == 9) {
                return 9;
            } else if (fIEVersion == 10) {
                return 10;
            } else {
                return 6;//IE版本<=7
            }
        } else if (isEdge) {
            return 'edge';//edge
        } else if (isIE11) {
            return 11; //IE11  
        } else {
            return -1;//不是ie浏览器
        }
    }

    this.isIE = function () {
        var ver = that.ieVersion();
        return typeof ver === 'number' && ver > 0;
    };

    /*
    * 将一个对象转换为popup组件可传递的参数数组
    * @param { data } 原始对象
    * @param { transfer } 转换方向, in, out
    * @returns 返回转换后的参数对象数组
    */
    this.toPopupParameter = function (data, transfer) {
        transfer = transfer || 'in';
        var parameter = [];
        var cur = {};
        for (var key in data) {
            cur = { transfer: transfer, value: data[key] };
            if (transfer.includes('in')) {
                cur.target = key;
            }
            if (transfer.includes('out')) {
                cur.source = key;
            }

            parameter.push(cur);
        }

        return parameter;
    };

    /*
    * 得到 popup parameter 参数值
    * @param { options } 原始参数
    */
    this.getPopupParameterValue = function (options) {
        if ($.com.isJqueryObject(options.opener) && $.com.isArray(options.parameter) && !$.com.hasValue(options.openerProperty)) {
            var formData = $.com.getFormData(options.opener) || {};
            for (var i = 0; i < options.parameter.length; i++) {
                var cur = options.parameter[i];
                if (cur.transfer === 'in') {
                    if ($.com.hasValue(cur.source) && !$.com.hasValue(cur.value)) {
                        cur.value = formData[cur.source];
                    }

                    if (!$.com.hasValue(cur.value)) {
                        var $input = options.opener.find('[' + _config_.attribute.field + '="' + cur.source + '"]');
                        if ($.com.isJqueryObject($input)) {
                            cur.value = $input.val() || $input.attr('value') || $input.attr('data-value');
                        }
                    }
                }
            }
        }
    };

    /*
    * 将一个 popup 参数转化为传入对象参数
    * @param { options } 原始参数
    */
    this.getFromPopupParameter = function (options) {
        options.inout = options.inout || {};
        options.formData = options.formData || {};

        if ($.com.isJqueryObject(options.opener)) {
            var formData = $.com.getFormData(options.opener) || {};
            //此时 parameter 和 inout 二选一存在, inout 伴随着 openerProperty 存在
            if ($.com.isArray(options.parameter)) {
                //parameter式的传递, 数据仅返回到了 opener 的 field中, 因此, 优先取 value, 其次取 opener.formData, 最后取 opener.field
                for (var i = 0; i < options.parameter.length; i++) {
                    var cur = options.parameter[i];
                    if (cur.transfer === 'in' && $.com.hasValue(cur.target)) {
                        if ($.com.hasValue(cur.source) && !$.com.hasValue(cur.value)) {
                            cur.value = formData[cur.source];

                            if (!$.com.hasValue(cur.value)) {
                                var $input = options.opener.find('[' + _config_.attribute.field + '="' + cur.source + '"]');
                                if ($.com.isJqueryObject($input)) {
                                    cur.value = $input.val() || $input.attr('value') || $input.attr('data-value');
                                }
                            }
                        }

                        cur.value = cur.value || null
                        if ($.com.hasValue(cur.value)) {
                            options.formData[cur.target] = cur.value;
                        }
                    }
                }
            } else if ($.com.hasValue(options.inout)) {
                //inout式的传递, 数据被同时保存到 openerProperty 属性和 opener 中的 field 中, 所以取值优先取 #:value, 其次取 openerProperty 对应的值, 其次取 opener.formData, 最后取 openner.field
                if ($.com.hasValue(options.openerProperty)) {
                    var data = formData[options.openerProperty] || [];
                    if (!$.com.isNullOrEmpty(data) && !$.com.isArray(data)) {
                        data = [data];
                    }

                    options.data = options.data || data || [];

                    var prefix = options.opener.attr(_config_.default.parameterPrefix) || _config_.default.parameterPrefixValue;
                    for (var key in options.inout) {
                        if (!$.com.hasValue(options.formData[key])) {
                            var source = options.inout[key];
                            var value = null;

                            if ($.com.hasValue(source) && typeof source === 'string' && source.startsWith(prefix)) {
                                value = that.eval(source, prefix);
                            }

                            if (!$.com.hasValue(value) && $.com.isArray(data)) {
                                value = data.select(key).join(', ');
                            }

                            if (!$.com.hasValue(value) && $.com.hasValue(formData)) {
                                //TBC ***** 值是否能直接当成字段使用, 如果值恰好就是字段名, 而并不想取字段值该如何?
                                value = $.com.getValue(formData, source);
                            }

                            if (!$.com.hasValue(value)) {
                                var $input = options.opener.find('[' + _config_.attribute.field + '="' + source + '"]');
                                if ($.com.isJqueryObject($input)) {
                                    value = $input.val() || $input.attr('value') || $input.attr('data-value');
                                }
                            }

                            if ($.com.hasValue(value)) {
                                options.formData[key] = value;
                            }
                        }
                    }
                }
            }
        }
    };

    /*1.用浏览器内部转换器实现html转码*/
    this.htmlEncode = function (html) {
        if ($.com.hasValue(html) && !html.includes('&lt;') && !html.includes('&gt;')) {
            //1.首先动态创建一个容器标签元素，如DIV
            var temp = document.createElement("div");
            //2.然后将要转换的字符串设置为这个元素的innerText(ie支持)或者textContent(火狐，google支持)
            (temp.textContent != undefined) ? (temp.textContent = html) : (temp.innerText = html);
            //3.最后返回这个元素的innerHTML，即得到经过HTML编码转换的字符串了
            var output = temp.innerHTML;
            temp = null;
            return output;
        }

        return html;
    };

    /*2.用浏览器内部转换器实现html解码*/
    this.htmlDecode = function (text) {
        if ($.com.hasValue(text) && text.includes('&lt;') && text.includes('&gt;')) {
            //1.首先动态创建一个容器标签元素，如DIV
            var temp = document.createElement("div");
            //2.然后将要转换的字符串设置为这个元素的innerHTML(ie，火狐，google都支持)
            temp.innerHTML = text;
            //3.最后返回这个元素的innerText(ie支持)或者textContent(火狐，google支持)，即得到经过HTML解码的字符串了。
            var output = temp.innerText || temp.textContent;
            temp = null;
            return output;
        }

        return text;
    };

    /* ie下将使用 url 转码 */
    this.encodeURI = function (url) {
        return encodeURI(url);
    };

    /* ie下将使用 url 解码 */
    this.decodeURI = function (url) {
        return decodeURI(url);
    };

    /*
    * blob 转 base64 的方法
    * @param { blob } blob对象, 如: file
    * @param { callback } 转换接收数据回调方法
    * @returns 在回调方法中返回转化后的数据
    */
    this.blobToBase64 = function (blob, callback) {
        var a = new FileReader();
        a.onload = function (e) {
            callback(e.target.result);
        }
        a.readAsDataURL(blob);
    };

    //替换字符串中逗号
    this.replaceMoney = function (money) {
        money = money.toString();
        var result = money.replace(/,/g, '');
        return result;
    };

    //金额转大写
    this.arabiaToChinese = function (num) {
        for (var i = num.length - 1; i >= 0; i--) {
            num = num.replace(",", ""); //替换tomoney()中的“,”
            num = num.replace(" ", ""); //替换tomoney()中的空格
        }

        num = num.replace("￥", ""); //替换掉可能出现的￥字符
        if (isNaN(num)) { //验证输入的字符是否为数字
            alert("请检查小写金额是否正确");
            return;
        }

        //---字符处理完毕，开始转换，转换采用前后两部分分别转换---//
        var part = String(num).split(".");
        var newchar = "";
        //小数点前进行转化
        for (var j = part[0].length - 1; j >= 0; j--) {
            //if (part[0].length > 10) {
            //    alert("位数过大，无法计算");
            //    return "";
            //} //若数量超过拾亿单位，提示
            var tmpnewchar = "";
            var perchar = part[0].charAt(j);
            switch (perchar) {
                case "0":
                    tmpnewchar = "零" + tmpnewchar;
                    break;
                case "1":
                    tmpnewchar = "壹" + tmpnewchar;
                    break;
                case "2":
                    tmpnewchar = "贰" + tmpnewchar;
                    break;
                case "3":
                    tmpnewchar = "叁" + tmpnewchar;
                    break;
                case "4":
                    tmpnewchar = "肆" + tmpnewchar;
                    break;
                case "5":
                    tmpnewchar = "伍" + tmpnewchar;
                    break;
                case "6":
                    tmpnewchar = "陆" + tmpnewchar;
                    break;
                case "7":
                    tmpnewchar = "柒" + tmpnewchar;
                    break;
                case "8":
                    tmpnewchar = "捌" + tmpnewchar;
                    break;
                case "9":
                    tmpnewchar = "玖" + tmpnewchar;
                    break;
            }

            switch (part[0].length - j - 1) {
                case 0:
                    tmpnewchar = tmpnewchar + "元";
                    break;
                case 1:
                    if (perchar !== 0) tmpnewchar = tmpnewchar + "拾";
                    break;
                case 2:
                    if (perchar !== 0) tmpnewchar = tmpnewchar + "佰";
                    break;
                case 3:
                    if (perchar !== 0) tmpnewchar = tmpnewchar + "仟";
                    break;
                case 4:
                    tmpnewchar = tmpnewchar + "万";
                    break;
                case 5:
                    if (perchar !== 0) tmpnewchar = tmpnewchar + "拾";
                    break;
                case 6:
                    if (perchar !== 0) tmpnewchar = tmpnewchar + "佰";
                    break;
                case 7:
                    if (perchar !== 0) tmpnewchar = tmpnewchar + "仟";
                    break;
                case 8:
                    tmpnewchar = tmpnewchar + "亿";
                    break;
                case 9:
                    tmpnewchar = tmpnewchar + "拾";
                    break;
            }

            newchar = tmpnewchar + newchar;
        }

        //小数点之后进行转化
        if (num.indexOf(".") !== -1) {
            if (part[1].length > 2) {
                //alert("小数点之后只能保留两位,系统将自动截段");
                part[1] = part[1].substr(0, 2);
            }
            for (var k = 0; k < part[1].length; k++) {
                var tmpnewchar = "";
                var perchar = part[1].charAt(k);
                switch (perchar) {
                    case "0":
                        tmpnewchar = "零" + tmpnewchar;
                        break;
                    case "1":
                        tmpnewchar = "壹" + tmpnewchar;
                        break;
                    case "2":
                        tmpnewchar = "贰" + tmpnewchar;
                        break;
                    case "3":
                        tmpnewchar = "叁" + tmpnewchar;
                        break;
                    case "4":
                        tmpnewchar = "肆" + tmpnewchar;
                        break;
                    case "5":
                        tmpnewchar = "伍" + tmpnewchar;
                        break;
                    case "6":
                        tmpnewchar = "陆" + tmpnewchar;
                        break;
                    case "7":
                        tmpnewchar = "柒" + tmpnewchar;
                        break;
                    case "8":
                        tmpnewchar = "捌" + tmpnewchar;
                        break;
                    case "9":
                        tmpnewchar = "玖" + tmpnewchar;
                        break;
                }
                if (k === 0) tmpnewchar = tmpnewchar + "角";
                if (k === 1) tmpnewchar = tmpnewchar + "分";
                newchar = newchar + tmpnewchar;
            }
        }

        //替换所有无用汉字
        while (newchar.search("零零") !== -1)
            newchar = newchar.replace("零零", "零");
        newchar = newchar.replace("零亿", "亿");
        newchar = newchar.replace("亿万", "亿");
        newchar = newchar.replace("零万", "万");
        newchar = newchar.replace("零元", "元");
        newchar = newchar.replace("零角", "");
        newchar = newchar.replace("零分", "");

        if (newchar.charAt(newchar.length - 1) === "元" || newchar.charAt(newchar.length - 1) === "角")
            newchar = newchar + "整";
        //  document.write(newchar);

        return newchar;
    };


    /*
    * 返回文件的路径, 适用于参数为文件路径的情况
    * @param { fileName } 文件名
    * @returns 返回文件的路径
    */
    this.getFilePath = function (fileName) {
        fileName = fileName || '';
        var index = fileName.lastIndexOf('/');
        if (index < 0) {
            var index = fileName.lastIndexOf('\\');
        }

        return fileName.substring(0, index);
    };

    /*
    * 返回文件的名称, 适用于参数为文件路径的情况
    * @param { fileName } 文件名
    * @returns 返回文件的名称
    */
    this.getFileName = function (fileName) {
        fileName = fileName || '';
        var index = fileName.lastIndexOf('/');
        if (index < 0) {
            var index = fileName.lastIndexOf('\\');
        }

        return fileName.substring(index + 1);
    };

    /*
    * 返回文件的相对路径文件名, 适用于参数为文件路径的情况
    * @param { fileName } 文件名
    * @returns 返回文件的相对路径文件名
    */
    this.getRelativePath = function (fileName) {
        var url = fileName;
        if (fileName.includes('//')) {
            url = new URL(fileName);
        }

        return fileName.replace(url.origin, '');
    };

    /*
    * 返回文件的扩展名
    * @param { fileName } 文件名
    * @returns 返回文件的扩展名
    */
    this.getFileExtension = function (fileName) {
        fileName = fileName || '';
        var index = fileName.lastIndexOf('.');
        return fileName.substring(index);
    };

    /*
    * 返回上传文件的扩展名对应的图标
    * @param { fileName } 文件名
    * @returns 返回对应的图标名称
    */
    this.getIconByFileExtension = function (fileName) {
        fileName = $.com.getFileExtension(fileName).toLowerCase();
        var result = '';
        switch (fileName) {
            case '.jpg':
            case '.jpeg':
            case '.png':
            case '.gif':
            case '.bmp':
                result = _config_.default.imageIcoFileName;
                break;
            case '.xls':
            case '.xlsx':
                result = 'xls';
                break;
            case '.doc':
            case '.docx':
                result = 'doc';
                break;
            case '.ppt':
            case '.pptx':
                result = 'ppt';
                break;
            case '.zip':
            case '.rar':
            case '.7z':
                result = 'zip';
                break;
            case '.html':
            case '.htm':
                result = 'html';
                break;
            case '.txt':
                result = 'txt';
                break;
            case '.pdf':
                result = 'pdf';
                break;
            default:
                result = 'default';
                break;
        }

        return result;
    };

    /*
    * 返回图片文件的缩略图
    * @param { fileName } 文件名
    * @returns 返回对应的文件名称
    */
    this.getImageThumbnailName = function (fileName) {
        var result = _config_.default.fileicoPath + "/" + _config_.default.imageIcoFileName + _config_.default.imageIcoFileExtension;
        if ($.com.hasValue(fileName)) {
            var ext = that.getIconByFileExtension(fileName);
            if (ext === _config_.default.imageIcoFileName) {
                if (fileName.lastIndexOf('\\') > 0) {
                    fileName = fileName.replace('\\', '/');
                }

                result = _config_.default.attachmentPath + "/" + that.getFilePath(fileName) + "/" + _config_.default.imageThumbnailPrefix + that.getFileName(fileName);
            } else {
                result = _config_.default.fileicoPath + "/" + ext + _config_.default.imageIcoFileExtension;
            }
        }

        return result;
    };

    /**
     * 获取文件类型图标, 用于在文件加载失败时显示文件的类型图标
     * @param {any} fileName
     */
    this.getIconFileName = fileName => _config_.default.fileicoPath + "/" + that.getIconByFileExtension(fileName) + _config_.default.imageIcoFileExtension;

    /*
    * 返回图片文件的地址, 如果不是预定义类型的图片文件, 则返回默认图片
    * @param { fileName } 文件名
    * @returns 返回对应的文件名称
    */
    this.getImageFileName = function (fileName) {
        var ext = that.getIconByFileExtension(fileName);
        if (ext === _config_.default.imageIcoFileName) {
            return _config_.default.attachmentPath + "/" + fileName;
        }

        return _config_.default.fileicoPath + "/" + ext + _config_.default.imageIcoFileExtension;
    };

    /*
    * 返回绑定到界面上的文件地址, 如果是个图片, 则显示图片的缩略图, 否则显示文件类型图标
    * @param { name } 文件原名
    * @returns 返回对应的文件地址
    */
    this.getAttachmentFileName = function (name, path) {
        var result = `${_config_.default.fileicoPath}/default.png`;
        if ($.com.hasValue(name)) {
            var ext = that.getIconByFileExtension(name);
            if (ext === _config_.default.imageIcoFileName && that.hasValue(path)) {
                if (name.lastIndexOf('\\') > 0) {
                    name = fileName.replace('\\', '/');
                }

                result = _config_.default.attachmentPath + "/" + that.getFilePath(path) + "/" + _config_.default.imageThumbnailPrefix + that.getFileName(path);
            } else {
                result = _config_.default.fileicoPath + "/" + ext + _config_.default.imageIcoFileExtension;
            }
        }

        return result;
    };

    /*
    * 返回上传文件名称不带后缀的固定长度名
    * @param { name } 文件原名
    * @param { length } 截取长度
    * @returns 返回对应的名称
    */
    this.getDisplayFileName = function (name, length) {
        if ($.com.hasValue(name)) {
            length = length || 16;
            if (name.length <= length) {
                length = name.length;
            }

            var index = name.lastIndexOf('.');
            return name.substring(0, length >= index ? index : length);
        }

        return name;
    };

    /**
     * 通用枚举类型格式化, 格式化 data 的 value 值
     * @param {any} data
     * @param {any} value
     * @param {any} options
     */
    this.commonFormatter = (data, value, options) => {
        var result = value;
        if (data && value) {
            if (data instanceof Array) {
                // data in array
                options = options || {};
                var valueField = options.dataValueField || _config_.field.defaults.dataValueField;
                var textField = options.dataTextField || _config_.field.defaults.dataTextField;
                if (value.includes(',')) {
                    let arr = [];
                    let values = value.split(',');
                    for (var index = 0; index < values.length; index++) {
                        const value = values[index].trim();
                        arr.push(data.firstValue(x => x[valueField] == value, textField));
                    }

                    result = arr.join(', ');
                }

                result = data.firstValue(x => x[valueField] == value, textField);
            } else {
                // data in object
                result = data[value.firstUpperCase()] || data[value.firstLowerCase()];
            }
        }

        return result || _config_.default.placeholder;
    }

    /**
     * bool 类型格式化显示, 格式化 data 定义的 value bool 值
     * @param {any} data
     * @param {any} value
     */
    this.booleanFormatter = (data, value) => data.firstValue(x => eval(x[_config_.field.defaults.dataValueField]) == value, _config_.field.defaults.dataTextField);
    /**
     * 百分比格式化显示
     * @param {any} value
     */
    this.percentFormatter = value => $.com.toFixed(($.com.toFixed(Number(value) || 0, 4) * 100), 2) + ' %';
    /**
     * 金额格式化显示
     * @param {any} value
     */
    this.amountFormatter = value => '￥' + $.com.toFixed(Number(value) || 0, 2);
    /**
     * 面积格式化显示
     * @param {any} value
     */
    this.areaFormatter = value => $.com.toFixed(Number(value) || 0, 2) + ' ㎡';
    /**
     * 图片文件格式化显示
     * @param {any} image
     * @param {any} alt
     */
    this.imageFormatter = (image, alt, size) => `<img src="${$.com.getImageFileName(image)}" alt="${alt || image}" onerror="javascript:this.src='${$.com.getIconFileName(image)}'" style="max-height:${size || _config_.default.imageFileSize}px; max-width:${size || _config_.default.imageFileSize}px;" />`;
    /**
     * 图片缩略图格式化显示
     * @param {any} image
     * @param {any} alt
     */
    this.imageThumbnailFormatter = (image, alt, size) => `<img src="${$.com.getImageThumbnailName(image)}" alt="${alt || image}" onerror="javascript:this.src='${$.com.getIconFileName(image)}'" style="height:${size || _config_.default.imageThumbnailSize}px" />`;

    /**
     * form 组件包含 business 相关字段时的数据绑定改变之前执行的方法
     * @param {any} el
     * @param {any} data
     */
    this.onFormWithBusinessDataChanging = (el, data) => {
        if ($.com.isJqueryObject(el.$form)) {
            let form = el.$form.data(_config_.form.namespace);
            if ($.com.isNullOrEmpty(data)) {
                data = $.extend(data, { businessType: form.options.data.businessType || form.options.field, businessKey: form.options.data.businessKey || form.options.data.id });
            }
        }
    };

    /**
     * 获取内容部分尺寸
     */
    this.getBodySize = () => {
        var $body = $('#_body_');
        return {
            height: $body.height(),
            width: $body.width()
        };
    };
};